package com.itwc.websocketdemo.websocket;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.itwc.websocketdemo.service.UserService;
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * 原生WebSocket服务器端点
 */
@Slf4j
@Component
@ServerEndpoint("/ws/{userId}")
public class NativeWebSocketServer {

    private static final Logger log = LoggerFactory.getLogger(NativeWebSocketServer.class);

    // 静态变量，用来记录当前在线连接数
    private static int onlineCount = 0;
    
    // concurrent包的线程安全Set，用来存放每个客户端对应的WebSocket对象
    private static CopyOnWriteArraySet<NativeWebSocketServer> webSocketSet = new CopyOnWriteArraySet<>();
    
    // 与某个客户端的连接会话，需要通过它来给客户端发送数据
    private Session session;
    
    // 接收userId
    private String userId = "";
    
    // 用户ID到WebSocket的映射
    private static ConcurrentHashMap<String, NativeWebSocketServer> webSocketMap = new ConcurrentHashMap<>();
    
    // JSON转换器
    private static final ObjectMapper objectMapper = new ObjectMapper();
    
    // Spring上下文，用于获取Service
    private static ApplicationContext applicationContext;
    
    /**
     * 设置Spring上下文
     */
    public static void setApplicationContext(ApplicationContext context) {
        applicationContext = context;
    }

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) {
        this.session = session;
        this.userId = userId;
        
        // 如果用户已经存在，先移除旧连接
        if (webSocketMap.containsKey(userId)) {
            webSocketMap.remove(userId);
            webSocketSet.remove(this);
            subOnlineCount();
        }
        
        webSocketSet.add(this);
        webSocketMap.put(userId, this);
        addOnlineCount();
        
        log.info("🔗 用户{}连接WebSocket成功，当前在线人数为：{}", userId, getOnlineCount());
        
        // 处理用户上线，包括离线补推
        try {
            Long userIdLong = Long.parseLong(userId);
            if (applicationContext != null) {
                UserService userService = applicationContext.getBean(UserService.class);
                userService.handleUserOnline(userIdLong, session.getId());
                log.info("🔗 用户 {} 已完成上线处理（原生WebSocket）", userIdLong);
            }
        } catch (NumberFormatException e) {
            log.error("🔗 用户ID格式错误: {}", userId, e);
        } catch (Exception e) {
            log.error("🔗 处理用户上线失败: {}", userId, e);
        }
        
        try {
            // 发送连接成功消息
            sendMessage(createMessage("connection", "连接成功", "WebSocket连接已建立"));
        } catch (IOException e) {
            log.error("❌ 用户{}发送连接成功消息失败", userId, e);
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        webSocketSet.remove(this);
        webSocketMap.remove(userId);
        subOnlineCount();
        log.info("🔌 用户{}断开WebSocket连接，当前在线人数为：{}", userId, getOnlineCount());
    }

    /**
     * 收到客户端消息后调用的方法
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("📨 收到用户{}的消息：{}", userId, message);
        
        try {
            // 解析客户端消息
            var messageObj = objectMapper.readTree(message);
            String type = messageObj.get("type").asText();
            
            switch (type) {
                case "auth":
                    // 身份验证消息
                    log.info("🔐 用户{}发送身份验证消息", userId);
                    
                    // 确保用户在线状态正确记录
                    try {
                        Long userIdLong = Long.parseLong(userId);
                        if (applicationContext != null) {
                            UserService userService = applicationContext.getBean(UserService.class);
                            userService.handleUserOnline(userIdLong, session.getId());
                            log.info("🔐 用户 {} 身份验证成功，已更新在线状态", userIdLong);
                        }
                    } catch (Exception e) {
                        log.error("❌ 处理用户{}身份验证失败", userId, e);
                    }
                    
                    sendMessage(createMessage("auth_success", "身份验证成功", "用户身份已确认"));
                    break;
                    
                case "heartbeat":
                    // 心跳消息
                    log.debug("💓 用户{}发送心跳消息", userId);
                    sendMessage(createMessage("heartbeat_response", "心跳响应", "服务器在线"));
                    break;
                    
                default:
                    log.warn("❓ 收到未知类型消息：{}", type);
                    sendMessage(createMessage("unknown", "未知消息类型", "服务器无法处理该消息类型"));
            }
        } catch (Exception e) {
            log.error("❌ 处理用户{}消息失败", userId, e);
        }
    }

    /**
     * 发生错误时调用
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("❌ 用户{}的WebSocket连接发生错误", userId, error);
    }

    /**
     * 实现服务器主动推送
     */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }

    /**
     * 发送自定义消息
     */
    public static void sendInfo(String message, @PathParam("userId") String userId) throws IOException {
        log.info("📤 发送消息到用户{}：{}", userId, message);
        if (userId != null && webSocketMap.containsKey(userId)) {
            webSocketMap.get(userId).sendMessage(message);
        } else {
            log.error("❌ 用户{}不在线", userId);
        }
    }
    
    /**
     * 发送推文通知给指定用户
     */
    public static void sendPostNotification(String userId, Long postId, String title, String content) {
        try {
            if (webSocketMap.containsKey(userId)) {
                String message = createPostMessage("new_post", postId, title, content);
                webSocketMap.get(userId).sendMessage(message);
                log.info("✅ 成功向用户{}发送推文通知：{}", userId, title);
            } else {
                log.warn("⚠️ 用户{}不在线，无法发送推文通知", userId);
            }
        } catch (Exception e) {
            log.error("❌ 向用户{}发送推文通知失败", userId, e);
        }
    }
    
    /**
     * 发送未读数量更新给指定用户
     */
    public static void sendUnreadCountUpdate(String userId, int unreadCount) {
        try {
            if (webSocketMap.containsKey(userId)) {
                String message = createUnreadCountMessage(unreadCount);
                webSocketMap.get(userId).sendMessage(message);
                log.info("✅ 成功向用户{}发送未读数量更新：{}", userId, unreadCount);
            } else {
                log.warn("⚠️ 用户{}不在线，无法发送未读数量更新", userId);
            }
        } catch (Exception e) {
            log.error("❌ 向用户{}发送未读数量更新失败", userId, e);
        }
    }
    
    /**
     * 广播消息给所有在线用户
     */
    public static void broadcastMessage(String title, String content) {
        String message = createMessage("broadcast", title, content);
        for (NativeWebSocketServer item : webSocketSet) {
            try {
                item.sendMessage(message);
            } catch (IOException e) {
                log.error("❌ 广播消息给用户{}失败", item.userId, e);
            }
        }
        log.info("📢 广播消息完成，在线用户数：{}", getOnlineCount());
    }
    
    /**
     * 创建消息JSON字符串
     */
    private static String createMessage(String type, String title, String content) {
        try {
            var message = objectMapper.createObjectNode();
            message.put("type", type);
            message.put("title", title);
            message.put("message", content);
            message.put("timestamp", System.currentTimeMillis());
            return objectMapper.writeValueAsString(message);
        } catch (Exception e) {
            log.error("❌ 创建消息JSON失败", e);
            return "{\"type\":\"error\",\"message\":\"消息创建失败\"}";
        }
    }
    
    /**
     * 创建推文消息JSON字符串（包含postId）
     */
    private static String createPostMessage(String type, Long postId, String title, String content) {
        try {
            var message = objectMapper.createObjectNode();
            message.put("type", type);
            message.put("postId", postId);
            message.put("title", title);
            message.put("message", content);
            message.put("timestamp", System.currentTimeMillis());
            return objectMapper.writeValueAsString(message);
        } catch (Exception e) {
            log.error("❌ 创建推文消息JSON失败", e);
            return "{\"type\":\"error\",\"message\":\"消息创建失败\"}";
        }
    }
    
    /**
     * 创建未读数量消息
     */
    private static String createUnreadCountMessage(int count) {
        try {
            var message = objectMapper.createObjectNode();
            message.put("type", "unread_count");
            message.put("count", count);
            message.put("timestamp", System.currentTimeMillis());
            return objectMapper.writeValueAsString(message);
        } catch (Exception e) {
            log.error("❌ 创建未读数量消息JSON失败", e);
            return "{\"type\":\"error\",\"message\":\"消息创建失败\"}";
        }
    }
    
    /**
     * 获取在线用户列表
     */
    public static ConcurrentHashMap<String, NativeWebSocketServer> getOnlineUsers() {
        return webSocketMap;
    }
    
    /**
     * 检查用户是否在线
     */
    public static boolean isUserOnline(String userId) {
        return webSocketMap.containsKey(userId);
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        NativeWebSocketServer.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        NativeWebSocketServer.onlineCount--;
    }
}